package com.iplant.base.utils.sql.orm;

import com.iplant.base.po.*;
import com.iplant.base.po.base.iPlantRequestBody;
import com.iplant.base.po.enums.ExceptionTypes;
import com.iplant.base.utils.cache.CacheUtils;
import com.iplant.base.utils.serializer.CloneTool;
import com.iplant.base.utils.general.GenericsUtils;
import com.iplant.base.utils.general.StringUtils;
import com.iplant.base.utils.sql.BaseDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author xiuto
 * 继承BasePO的实体类必须使用注解DBBasePO
 */
@Service("PoServiceImpl")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PoServiceImpl<T extends BasePO> implements PoService<T> {

    private static final Logger logger = LoggerFactory.getLogger(PoServiceImpl.class);
    private BaseDAO mBaseDAO;


    private static Map<Class<? extends BasePO>, BaseDAO> BaseDAOMap = new HashMap<>();


    public static Map<Class<? extends BasePO>, Class<? extends BaseDAO>> BaseDAOClassMap = new HashMap<>();


    private static BaseDAO CreateBaseDAO(Class<? extends BasePO> wClazz) {
        if (wClazz == null || !BaseDAOClassMap.containsKey(wClazz)) {
            return new BaseDAO(wClazz);
        }

        synchronized (wClazz) {
            if (BaseDAOMap.containsKey(wClazz))
                return BaseDAOMap.get(wClazz);

            Class<? extends BaseDAO> wBaseDAOClazz = BaseDAOClassMap.get(wClazz);

            BaseDAO wResult = POUtils.GetInstance().InitDao(wBaseDAOClazz);

            if (wResult == null) {
                wResult = new BaseDAO(wClazz);
            }

            BaseDAOMap.put(wClazz, wResult);

            return wResult;
        }

    }


    public BaseDAO GetBaseDAO() {
        if (mBaseDAO == null) {
            synchronized (this) {
                if (mBaseDAO == null)
                    mBaseDAO = PoServiceImpl.CreateBaseDAO(this.getTClass());
            }
        }
        return mBaseDAO;
    }


    private T mDefaultValue = null;

    private Class<T> mPOClazz = null;


    @SuppressWarnings("unchecked")
    public Class<T> getTClass() {
        if (mPOClazz == null) {
            mPOClazz = GenericsUtils.getSuperClassGenericType(this.getClass(), 0);
        }

        return mPOClazz;
    }

    public PoServiceImpl(Class<T> wClazz) {
        super();
        mPOClazz = wClazz;
    }


    @Override
    public List<T> SelectPOList(
            BaseUser wLoginUser, Map<String, Object> wParams, Pagination wPagination, OutResult<Integer> wErrorCode) {
        if (wParams == null)
            wParams = new HashMap<>();
        return GetBaseDAO().SelectPOList(wLoginUser, wParams, getTClass(), wPagination, wErrorCode);
    }

    @Override
    public List<T> SelectPOList(
            BaseUser wLoginUser, Map<String, Object> wParams, OutResult<Integer> wErrorCode) {
        if (wParams == null)
            wParams = new HashMap<>();
        return GetBaseDAO().SelectPOList(wLoginUser, wParams, getTClass(), wErrorCode);
    }

    @Override
    public ServiceResult<List<T>> SelectPOList(BaseUser wLoginUser, Map<String, Object> wParams,
                                               Pagination wPagination) {
        ServiceResult<List<T>> wResult = new ServiceResult<>();
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        if (wParams == null)
            wParams = new HashMap<>();
        wResult.Result = this.SelectPOList(wLoginUser, wParams, wPagination, wErrorCode);

        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public ServiceResult<List<T>> SelectPOList(BaseUser wLoginUser, Map<String, Object> wParams) {
        ServiceResult<List<T>> wResult = new ServiceResult<>();
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        if (wParams == null)
            wParams = new HashMap<>();
        wResult.Result = this.SelectPOList(wLoginUser, wParams, wErrorCode);

        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }


    public void SetItemList(BaseUser wLoginUser, List<T> wResult, OutResult<Integer> wErrorCode) {
        if (wResult == null || wResult.size() == 0)
            return;
        GetBaseDAO().SetItemList(wLoginUser, wResult, wErrorCode);
    }

    @Override
    public List<T> SelectPOList(
            BaseUser wLoginUser, List<Integer> wIDList, Map<String, Object> wParams,
            Pagination wPagination, OutResult<Integer> wErrorCode) {
        if (wParams == null)
            wParams = new HashMap<>();
        List<T> wResult = GetBaseDAO().SelectPOList(wLoginUser, wIDList, null, wParams, getTClass(), wPagination,
                wErrorCode);
        CacheUtils.SetData(wResult);
        return wResult;
    }

    @Override
    public ServiceResult<List<T>> SelectPOList(BaseUser wLoginUser, List<Integer> wIDList,
                                               Map<String, Object> wParams, Pagination wPagination) {
        ServiceResult<List<T>> wResult = new ServiceResult<>();
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        if (wParams == null)
            wParams = new HashMap<>();
        wResult.Result = this.SelectPOList(wLoginUser, wIDList, wParams, wPagination, wErrorCode);
        CacheUtils.SetData(wResult.Result);
        wResult.AddFaultCode(wErrorCode);
        this.SetItemList(wLoginUser, wResult.Result, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }


    @Override
    public T SelectPO(
            BaseUser wLoginUser, Map<String, Object> wParams, OutResult<Integer> wErrorCode) {
        if (wParams == null)
            wParams = new HashMap<>();
        List<T> wList = this.SelectPOList(wLoginUser, wParams, null, wErrorCode);

        if (wErrorCode.Result != 0 || wList == null || wList.size() == 0) {
            try {
                if (mDefaultValue == null) {
                    mDefaultValue = getTClass().newInstance();
                }
            } catch (Exception e) {
                logger.error("SelectPO", e);
            }
            return mDefaultValue;
        }
        CacheUtils.SetData(wList);

        this.SetItemList(wLoginUser, StringUtils.parseListArgs(wList.get(0)), wErrorCode);

        return wList.get(0);
    }

    @Override
    public ServiceResult<T> SelectPO(BaseUser wLoginUser, Map<String, Object> wParams) {
        ServiceResult<T> wResult = new ServiceResult<>();
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        if (wParams == null)
            wParams = new HashMap<>();
        wResult.Result = this.SelectPO(wLoginUser, wParams, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }


    @Override
    public ServiceResult<T> SelectPO(
            BaseUser wLoginUser, int wID, String wCode) {
        ServiceResult<T> wResult = new ServiceResult<>();

        OutResult<Integer> wErrorCode = new OutResult<>(0);
        wResult.Result = GetBaseDAO().SelectPO(wLoginUser, wID, wCode, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public T SelectPO(
            BaseUser wLoginUser, int wID, String wCode, OutResult<Integer> wErrorCode) {
        return GetBaseDAO().SelectPO(wLoginUser, wID, wCode, wErrorCode);
    }


    @Override
    public int DeletePO(BaseUser wLoginUser, T wBasePO, OutResult<Integer> wErrorCode) {

        return GetBaseDAO().DeletePO(wLoginUser, wBasePO, wErrorCode);
    }

    @Override
    public int DeletePOList(BaseUser wLoginUser, List<T> wBasePOList, OutResult<Integer> wErrorCode) {

        return GetBaseDAO().DeletePOList(wLoginUser, wBasePOList, wErrorCode);
    }

    @Override
    public int Delete(BaseUser wLoginUser, Map<String, Object> wData, String... wOtherCondition) {
        return GetBaseDAO().Delete(wData, wOtherCondition);
    }

    @Override
    public int Delete(BaseUser wLoginUser, String wMustCondition, String... wOtherCondition) {
        return GetBaseDAO().Delete(wMustCondition, wOtherCondition);
    }

    @Override
    public ServiceResult<Integer> DeletePO(BaseUser wLoginUser, T wBasePO) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);

        wResult.Result = GetBaseDAO().DeletePO(wLoginUser, wBasePO, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public ServiceResult<Integer> DeletePO(BaseUser wLoginUser, T wBasePO,Boolean wIsSuper) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);

        wResult.Result = GetBaseDAO().DeletePO(wLoginUser, wBasePO, wIsSuper,wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public ServiceResult<Integer> DeletePOList(BaseUser wLoginUser, List<T> wBasePOList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);

        wResult.Result = GetBaseDAO().DeletePOList(wLoginUser, wBasePOList, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public ServiceResult<Integer> DeletePOList(BaseUser wLoginUser, List<T> wBasePOList,Boolean wIsSuper) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);

        wResult.Result = GetBaseDAO().DeletePOList(wLoginUser, wBasePOList, wIsSuper,wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public ServiceResult<Integer> DeletePO(BaseUser wLoginUser, Map<String, Object> wParam) {
        if (wParam == null)
            wParam = new HashMap<>();
        if (!wParam.containsKey("data")) {
            ServiceResult<Integer> wServiceResult = new ServiceResult<>(0);
            wServiceResult.AddFaultCode(ExceptionTypes.Parameter);
            return wServiceResult;
        }
        Boolean wIsSuper=false;
        if (wParam.containsKey("super")) {
            wIsSuper=StringUtils.parseBoolean(wParam.get("super"));
        }
        if(wLoginUser.Grad<=0){
            wIsSuper=false;
        }
        T wT = CloneTool.Clone(wParam.get("data"), mPOClazz);
        return this.DeletePO(wLoginUser, wT,wIsSuper);
    }

    @Override
    public ServiceResult<Integer> DeletePOList(BaseUser wLoginUser, Map<String, Object> wParam) {
        if (wParam == null)
            wParam = new HashMap<>();

        if (!wParam.containsKey("data")) {
            ServiceResult<Integer> wServiceResult = new ServiceResult<>(0);
            wServiceResult.AddFaultCode(ExceptionTypes.Parameter);
            return wServiceResult;
        }

        List<T> wList = CloneTool.CloneArray(wParam.get("data"), mPOClazz);

        Boolean wIsSuper=false;
        if (wParam.containsKey("super")) {
            wIsSuper=StringUtils.parseBoolean(wParam.get("super"));
        }
        if(wLoginUser.Grad<=0){
            wIsSuper=false;
        }

        ServiceResult<Integer> wServiceResult = this.DeletePOList(wLoginUser, wList,wIsSuper);

        return wServiceResult;
    }

    @Override
    public synchronized int UpdateCheckPo(BaseUser wLoginUser, T wBasePO, OutResult<Integer> wErrorCode) {
        return GetBaseDAO().UpdateCheckPo(wLoginUser, wBasePO, wErrorCode);
    }

    @Override
    public synchronized int UpdatePo(BaseUser wLoginUser, T wBasePO, OutResult<Integer> wErrorCode) {
        return GetBaseDAO().UpdatePo(wLoginUser, wBasePO, wErrorCode);
    }

    @Override
    public synchronized int UpdatePo(BaseUser wLoginUser, T wBasePO, Boolean wIsCheck, OutResult<Integer> wErrorCode) {
        return GetBaseDAO().UpdatePo(wLoginUser, wBasePO, wIsCheck, wErrorCode);
    }

    @Override
    public synchronized ServiceResult<Integer> UpdatePo(BaseUser wLoginUser, T wBasePO) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        wResult.Result = GetBaseDAO().UpdatePo(wLoginUser, wBasePO, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public synchronized ServiceResult<Integer> UpdateCheckPo(BaseUser wLoginUser, T wBasePO) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        wResult.Result = GetBaseDAO().UpdatePo(wLoginUser, wBasePO, true, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public synchronized ServiceResult<T> UpdateSelectPo(BaseUser wLoginUser, T wBasePO) {
        ServiceResult<T> wResult = new ServiceResult<>();
        wResult.Result = wBasePO;

        OutResult<Integer> wErrorCode = new OutResult<>(0);
        GetBaseDAO().UpdatePo(wLoginUser, wBasePO, wErrorCode);
        if (wErrorCode.Result != 0) {
            wResult.AddFaultCode(wErrorCode);
        } else {
            Map<String, Object> wParam = new HashMap<>();
            wParam.put("ID", wBasePO.ID);
            return this.SelectPO(wLoginUser, wParam);
        }
        return wResult;
    }


    @Override
    public synchronized ServiceResult<T> UpdatePo(BaseUser wLoginUser, Map<String, Object> wParam) {
        ServiceResult<T> wResult = new ServiceResult<>();
        if (wParam == null)
            wParam = new HashMap<>();

        if (!wParam.containsKey("data")) {
            wResult.AddFaultCode(ExceptionTypes.Parameter);
            return wResult;
        }
        T wT = CloneTool.Clone(wParam.get("data"), mPOClazz);

        int wIsNotCheck = wParam.containsKey("IsNotCheck") ? (int) wParam.get("IsNotCheck") : 0;

        wResult.Result = wT;
        wT.setUserInfo(wLoginUser);
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        GetBaseDAO().UpdatePo(wLoginUser, wT, wIsNotCheck != 1, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public synchronized ServiceResult<T> UpdatePo(BaseUser wLoginUser, iPlantRequestBody<T> wBody) {
        ServiceResult<T> wResult = new ServiceResult<>();
        if (wBody == null)
            wBody = new iPlantRequestBody();


        T wT = wBody.getData();
        if (wT == null) {
            wResult.AddFaultCode(ExceptionTypes.Parameter);
            return wResult;
        }

        int wIsNotCheck = wBody.containsKey("IsNotCheck") ? (int) wBody.get("IsNotCheck") : 0;

        wResult.Result = wT;
        wT.setUserInfo(wLoginUser);
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        GetBaseDAO().UpdatePo(wLoginUser, wT, wIsNotCheck != 1, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }


    @Override
    public ServiceResult<T> UpdateSelectPo(BaseUser wLoginUser, Map<String, Object> wParam) {
        ServiceResult<T> wResult = this.UpdatePo(wLoginUser, wParam);

        if (StringUtils.isEmpty(wResult.FaultCode) && wResult.Result != null && wResult.Result.ID > 0) {
            wParam = new HashMap<>();
            wParam.put("ID", wResult.Result.ID);
            return this.SelectPO(wLoginUser, wParam);
        }
        return wResult;
    }


    @Override
    public synchronized List<T> UpdatePoList(BaseUser wLoginUser, List<T> wList, OutResult<Integer> wErrorCode) {
        List<T> wResult = wList;
        if (wList == null || wList.size() == 0) {
            wErrorCode.Result = ExceptionTypes.Parameter.getValue();
            return wResult;
        }
        this.GetBaseDAO().UpdatePoList(wLoginUser, wList, wErrorCode);

        return wResult;
    }


    @Override
    public synchronized ServiceResult<List<T>> UpdatePoList(BaseUser wLoginUser, iPlantRequestBody<List<T>> wBody) {
        ServiceResult<List<T>> wResult = new ServiceResult<>();
        if (wBody == null)
            wBody = new iPlantRequestBody();


        wResult.Result = wBody.getData();
        if (wResult.Result == null || wResult.Result.size() == 0) {
            wResult.AddFaultCode(ExceptionTypes.Parameter);
            return wResult;
        }

        int wIsNotCheck = wBody.containsKey("IsNotCheck") ? (int) wBody.get("IsNotCheck") : 0;
        int wIsNotAlone = wBody.containsKey("IsNotAlone") ? (int) wBody.get("IsNotAlone") : 0;
        OutResult<Integer> wErrorCode = new OutResult<>(0);

        this.GetBaseDAO().UpdatePoList(wLoginUser, wResult.Result, wIsNotAlone != 1, wIsNotCheck != 1, wErrorCode);

        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }


    @Override
    public synchronized ServiceResult<List<T>> UpdatePoList(BaseUser wLoginUser, Map<String, Object> wParam) {
        ServiceResult<List<T>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        if (wParam == null)
            wParam = new HashMap<>();

        if (!wParam.containsKey("data")) {
            wResult.AddFaultCode(ExceptionTypes.Parameter);
            return wResult;
        }

        wResult.Result = CloneTool.CloneArray(wParam.get("data"), mPOClazz);

        int wIsNotCheck = wParam.containsKey("IsNotCheck") ? (int) wParam.get("IsNotCheck") : 0;

        int wIsNotAlone = wParam.containsKey("IsNotAlone") ? (int) wParam.get("IsNotAlone") : 0;

        OutResult<Integer> wErrorCode = new OutResult<>(0);

        this.GetBaseDAO().UpdatePoList(wLoginUser, wResult.Result, wIsNotAlone != 1, wIsNotCheck != 1, wErrorCode);

        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }


    @Override
    public int ActivePO(
            BaseUser wLoginUser, T wBasePO, int wActive, OutResult<Integer> wErrorCode) {
        return GetBaseDAO().ActivePO(wLoginUser, wBasePO, wActive, wErrorCode);
    }

    @Override
    public int ActivePOList(
            BaseUser wLoginUser, List<T> wBasePOList, int wActive, OutResult<Integer> wErrorCode) {
        return GetBaseDAO().ActivePOList(wLoginUser, wBasePOList, wActive, wErrorCode);
    }

    @Override
    public ServiceResult<Integer> ActivePO(BaseUser wLoginUser, T wBasePO, int wActive) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        wResult.Result = GetBaseDAO().ActivePO(wLoginUser, wBasePO, wActive, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public ServiceResult<Integer> ActivePOList(BaseUser wLoginUser, List<T> wBasePOList, int wActive) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        wResult.Result = GetBaseDAO().ActivePOList(wLoginUser, wBasePOList, wActive, wErrorCode);
        wResult.AddFaultCode(wErrorCode);
        return wResult;
    }

    @Override
    public ServiceResult<Integer> ActivePOList(
            BaseUser wLoginUser, Map<String, Object> wParam) {
        if (wParam == null)
            wParam = new HashMap<>();
        int wActive = wParam.containsKey("Active") ? (int) wParam.get("Active") : 0;

        List<T> wList = CloneTool.CloneArray(wParam.get("data"), mPOClazz);
        return this.ActivePOList(wLoginUser, wList, wActive);
    }

    @Override
    public ServiceResult<Integer> ActivePO(
            BaseUser wLoginUser, Map<String, Object> wParam) {

        if (wParam == null)
            wParam = new HashMap<>();
        if (!wParam.containsKey("data")) {
            ServiceResult<Integer> wServiceResult = new ServiceResult<>(0);
            wServiceResult.AddFaultCode(ExceptionTypes.Parameter);
            return wServiceResult;
        }
        int wActive = wParam.containsKey("Active") ? (int) wParam.get("Active") : 0;
        T wT = CloneTool.Clone(wParam.get("data"), mPOClazz);
        return this.ActivePO(wLoginUser, wT, wActive);
    }

    @Override
    public ServiceResult<Integer> ActivePOChange(
            BaseUser wLoginUser, Map<String, Object> wParam) {
        ServiceResult<Integer> wServiceResult = new ServiceResult<>(0);
        if (wParam == null)
            wParam = new HashMap<>();
        if (!wParam.containsKey("data")) {
            wServiceResult.AddFaultCode(ExceptionTypes.Parameter);
            return wServiceResult;
        }
        Map<String, Object> wData = CloneTool.Clone(wParam.get("data"), Map.class);
        String wProp = "";
        if (wParam.containsKey("Prop_Name")) {
            wProp = StringUtils.parseString(wParam.get("Prop_Name"));
        }
        int wActive = 1;
        if (wParam.containsKey("Active")) {
            wActive = StringUtils.parseInt(wParam.get("Active"));
        }
        int wDeActive = 2;
        if (wParam.containsKey("DeActive")) {
            wDeActive = StringUtils.parseInt(wParam.get("DeActive"));
        }
        OutResult<Integer> wErrorCode = new OutResult<>(0);
        wServiceResult.Result = this.ActivePOChange(wLoginUser, wData, wProp, wActive, wDeActive, wErrorCode);
        wServiceResult.AddFaultCode(wErrorCode);
        return wServiceResult;
    }

    @Override
    public int ActivePOChange(
            BaseUser wLoginUser, Map<String, Object> wParams, String wProp, int wActive, int wDeActive,
            OutResult<Integer> wErrorCode) {
        int wResult = 0;
        if (wParams == null || wParams.size() == 0 || !wParams.containsKey("ID")) {

            wErrorCode.Result = ExceptionTypes.Parameter.getValue();
            return wResult;
        }
        if (wActive == wDeActive) {
            wErrorCode.Result = ExceptionTypes.Parameter.getValue();
            return wResult;
        }
        int wID = StringUtils.parseInt(wParams.get("ID"));
        if (wID <= 0) {
            wErrorCode.Result = ExceptionTypes.Parameter.getValue();
            return wResult;
        }
        wParams.remove("ID");

        Map<String, Object> wData = new HashMap<>();
        if (StringUtils.isEmpty(wProp))
            wProp = "Active";
        wData.put(wProp, wDeActive);
        wData.put("EditorID", wLoginUser.getID());
        wData.put("EditTime", Calendar.getInstance());

        wResult += GetBaseDAO().Update(wData, wParams, " and ID >0   AND " + wProp + "!=" + wDeActive);


        wData.put(wProp, wActive);
        wParams.clear();
        wParams.put("ID", wID);

        wResult += GetBaseDAO().Update(wData, wParams, "  AND " + wProp + "!=" + wActive);

        return wResult;
    }
}
